﻿
using Cms.Core.Data.Infrastructure;
using Cms.Core.Infrastructure.Common;
using Cms.Core.Infrastructure.Dependency;
using Cms.Core.Infrastructure.Entity;
using Cms.Core.Infrastructure.Extension;
using Cms.Core.Infrastructure.Log;
using Cms.Core.Infrastructure.Runtime.Session;
using Cms.Core.Infrastructure.Ui;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Z.EntityFramework.Plus;

namespace Cms.Core.Data.UnitOfWork
{
    public class Repository<TEntity> : Repository<TEntity,Guid>, IRepository<TEntity> where TEntity : class, IEFEntityBase
    {
        public Repository(IUnitOfWork unitOfWork, ICmsSession session) : base(unitOfWork, session)
        {
      
        }

    }
    public class Repository<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> where TEntity : class, IEFEntityBase<TPrimaryKey>
    {
        private readonly DbContext _dbContext;
        private readonly DbSet<TEntity> _dbSet;
        private readonly IUnitOfWork _unitOfWork;

        private readonly ILoggerHelper _loggerHelper = null;

        public IQueryable<TEntity> Entities => _dbSet.AsQueryable().AsNoTracking();

        public IQueryable<TEntity> TrackEntities => _dbSet.AsQueryable();

        /// <summary>
        /// 查询不删除
        /// </summary>
        public virtual IQueryable<TEntity> QueryNotDelete => Entities.Where(o => o.IsDelete == false);

        /// <summary>
        /// 查询已删除
        /// </summary>

        public virtual IQueryable<TEntity> QueryDelete => Entities.Where(o => o.IsDelete == true);


        private readonly ICmsSession _session = null;

        public Repository(IUnitOfWork unitOfWork, ICmsSession session)
        {
            this._unitOfWork = unitOfWork;
            this._dbContext = unitOfWork.GetContext(); 
            this._dbSet = unitOfWork.GetContext().Set<TEntity>();
            _loggerHelper = ServiceLocator.Instance.GetService<ILoggerHelper>();
            this._session = session;
            
        }

        public virtual IQueryable<TEntity> TrackingQuery(Expression<Func<TEntity, bool>> predicate)
        {
            predicate.NotNull(nameof(predicate));
            return TrackingQuery().Where(predicate);
        }

        public virtual IQueryable<TEntity> TrackingQuery()
        {
            return TrackEntities;
        }

        public virtual IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate)
        {

            predicate.NotNull(nameof(predicate));
            return Query().Where(predicate);
        }


        public virtual IQueryable<TEntity> Query()
        {
            return Entities;
        }


        public virtual IQueryable<EntityDto> TrackingQueryToDto<EntityDto>(Expression<Func<EntityDto, bool>> predicate) where EntityDto : IEntityDto<TPrimaryKey>
        {
            predicate.NotNull(nameof(predicate));
            return TrackingQueryToDto<EntityDto>().Where(predicate);
        }

        public virtual IQueryable<EntityDto> TrackingQueryToDto<EntityDto>() where EntityDto : IEntityDto<TPrimaryKey>
        {
            return TrackEntities.ToDto<TEntity, EntityDto>();
        }



        public virtual IQueryable<EntityDto> QueryToDto<EntityDto>(Expression<Func<EntityDto, bool>> predicate) where EntityDto : IEntityDto<TPrimaryKey>
        {
            predicate.NotNull(nameof(predicate));
            return QueryToDto<EntityDto>().Where(predicate);
        }


        public virtual IQueryable<EntityDto> QueryToDto<EntityDto>() where EntityDto : IEntityDto<TPrimaryKey>
        {
            return Entities.ToDto<TEntity, EntityDto>();
        }


        public virtual TEntity Get(TPrimaryKey id)
        {
            id.NotNull(nameof(id));
            return _dbSet.Find(id);
        }

        public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate)
        {
            predicate.NotNull(nameof(predicate));
            return _dbSet.Where(predicate).FirstOrDefault();
        }

        public virtual async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate)
        {
            predicate.NotNull(nameof(predicate));
            return await _dbSet.FirstOrDefaultAsync(predicate);
        }



        public virtual async Task<TEntity> GetAsync(TPrimaryKey id)
        {
            id.NotNull(nameof(id));
            return await _dbSet.FindAsync(id);
        }

        public virtual EntityDto GetDto<EntityDto>(TPrimaryKey id) where EntityDto : class,IEntityDto<TPrimaryKey>, new()
        {
            id.NotNull(nameof(id));
            return Get(id).MapTo<EntityDto>();
        }

        public virtual async Task<EntityDto> GetDtoAsync<EntityDto>(TPrimaryKey id) where EntityDto : class, IEntityDto<TPrimaryKey>, new()
        {
            id.NotNull(nameof(id));
            var entitnDot = await GetAsync(id);
            return entitnDot.MapTo<EntityDto>();
        }


        public virtual async Task<EntityDto> GetDtoAsync<EntityDto>(Expression<Func<TEntity, bool>> predicate) where EntityDto : class, IEntityDto, new()
        {
            predicate.NotNull(nameof(predicate));
            var entitnDot = await _dbSet.FirstOrDefaultAsync(predicate);
            if (entitnDot == null)
            {
                return default(EntityDto);
            }
            return entitnDot.MapTo<EntityDto>();
        }





        public virtual IQueryable<TEntity> FromSql(string sql, params object[] parameters)
        {
            
            return _dbSet.FromSql(new RawSqlString(sql), parameters);
        }

        /// <summary>
        /// 异步插入实体
        /// </summary>
        /// <param name="entities">实体对象集合</param>
        /// <returns>操作影响的行数</returns>
        //public async Task<int> InsertAsync(params TEntity[] entities)
        //{
        //    entities.NotNull(nameof(entities));

        //    for (int i = 0; i < entities.Length; i++)
        //    {
        //        TEntity entity = entities[i];
        //        entities[i] = CheckCreateTime(entity);
        //    }

          
        //    await _dbSet.AddRangeAsync(entities);
        //    return await _dbContext.SaveChangesAsync();
        //}

        /// <summary>
        /// 异步插入实体
        /// </summary>
        /// <param name="entities">实体对象集合</param>
        /// <returns>操作影响的行数</returns>
        public async Task<OperationResult> InsertAsync(params TEntity[] entities)
        {
            entities.NotNull(nameof(entities));

            for (int i = 0; i < entities.Length; i++)
            {
                TEntity entity = entities[i];
                entities[i] = CheckCreateTime(entity);
            }


            await _dbSet.AddRangeAsync(entities);
            try
            {
                int count = await _dbContext.SaveChangesAsync();
                return new OperationResult(count > 0 ? "添加成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
            }
            catch (Exception ex)
            {

                throw ex;
            }
        }


        public virtual async Task<OperationResult> InsertAsync<EntityDto>(EntityDto dto, Func<EntityDto, Task> checkFunc = null, 
            Func<EntityDto, TEntity, Task<TEntity>> insertFunc = null,Func<TEntity, EntityDto> completeFunc = null) where EntityDto : IEntityDto<TPrimaryKey>
        {

            dto.NotNull(nameof(dto));
            try
            {
                if (!checkFunc.IsNull())
                {
                    await checkFunc(dto);
                }
                TEntity entity = dto.MapTo<TEntity>();
            
                if (!insertFunc.IsNull())
                {
                    entity = await insertFunc(dto, entity);
                }
                entity = CheckCreateTime(entity);
                await _dbSet.AddAsync(entity);

                if (completeFunc.IsNotNull())
                {
                    dto = completeFunc(entity);
                }
                dto.Id = entity.Id;

            }
            catch (CustomException e)
            {
                return new OperationResult(e.Message, OperationResultType.Error);
            }
            catch (Exception e)
            {
                //加日志
                _loggerHelper.Error(typeof(IRepository<TEntity, TPrimaryKey>), e);
                return new OperationResult(e.Message, OperationResultType.Exp);
            }
            try
            {
                int count = await _dbContext.SaveChangesAsync();
                return new OperationResult(count > 0 ? "添加成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
            }
            catch (Exception ex)
            {
                _loggerHelper.Error(typeof(IRepository<TEntity, TPrimaryKey>), ex);
                throw ex;
            }
          
        }

        /// <summary>
        /// 添加 
        /// </summary>
        /// <typeparam name="EntityDto"></typeparam>
        /// <param name="dto"></param>
        /// <returns></returns>

        public virtual OperationResult Insert<EntityDto>(EntityDto dto) where EntityDto : IEntityDto<TPrimaryKey>
        {

            dto.NotNull(nameof(dto));
            try
            {
         
                TEntity entity = dto.MapTo<TEntity>();

             
                entity = CheckCreateTime(entity);
                 _dbSet.Add(entity);
            }
            catch (CustomException e)
            {
                return new OperationResult(e.Message, OperationResultType.Error);
            }
            catch (Exception e)
            {
                //加日志
                _loggerHelper.Error(typeof(IRepository<TEntity, TPrimaryKey>), e);
                return new OperationResult(e.Message, OperationResultType.Exp);
            }
            try
            {
                int count = _dbContext.SaveChanges();
                return new OperationResult(count > 0 ? "添加成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }

        public virtual async Task<OperationResult> InsertAsync(TEntity entity) 
        {

            entity.NotNull(nameof(entity));
            try
            {
              
                entity = CheckCreateTime(entity);
                await _dbSet.AddAsync(entity);

            }
            catch (CustomException e)
            {
                return new OperationResult(e.Message, OperationResultType.Error);
            }
            catch (Exception e)
            {
                //加日志
                _loggerHelper.Error(typeof(IRepository<TEntity, TPrimaryKey>), e);
                return new OperationResult(e.Message, OperationResultType.Exp);
            }
            try
            {
                int count = await _dbContext.SaveChangesAsync();
                return new OperationResult(count > 0 ? "添加成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }



        //public virtual async Task<OperationResult<TEntity>> InsertAsync<EntityDto>(EntityDto dto, Func<EntityDto, Task> checkFunc = null, Func<EntityDto, TEntity, Task<TEntity>> insertFunc = null) where EntityDto : IEntityDto<TPrimaryKey>
        //{

        //    dto.NotNull(nameof(dto));
        //    TEntity entity = default(TEntity);
        //    try
        //    {
        //        if (!checkFunc.IsNull())
        //        {
        //            await checkFunc(dto);
        //        }
        //        entity = dto.MapTo<TEntity>();

        //        if (!insertFunc.IsNull())
        //        {
        //            entity = await insertFunc(dto, entity);
        //        }
        //        entity = CheckCreateTime(entity);
        //        await _dbSet.AddAsync(entity);

        //    }
        //    catch (CustomException e)
        //    {
        //        return new OperationResult<TEntity>(e.Message, entity, OperationResultType.Error);
        //    }
        //    catch (Exception e)
        //    {
        //        //加日志
        //        _loggerHelper.Error(typeof(IRepository<TEntity, TPrimaryKey>), e);
        //        return new OperationResult<TEntity>(e.Message, entity, OperationResultType.Exp);
        //    }
        //    try
        //    {
        //        int count = await _dbContext.SaveChangesAsync();
        //        return new OperationResult<TEntity>(count > 0 ? "添加成功" : "操作没有引发任何变化", entity, count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
        //    }
        //    catch (Exception ex)
        //    {

        //        throw ex;
        //    }

        //}
        /// <summary>
        /// 检查创建时间
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        private  TEntity CheckCreateTime(TEntity entity)
        {
            entity.CreateTime = DateTime.Now;
            return entity;
        }






        #region 更新
        public virtual async Task<OperationResult> UpdateAsync<EntityDto>(EntityDto dto, Func<EntityDto, TEntity, Task> checkFunc = null, Func<EntityDto, TEntity, Task<TEntity>> updateFunc = null) where EntityDto : class, IEntityDto<TPrimaryKey>, new()
        {
            dto.NotNull(nameof(dto));
            try
            {

                var entity = await GetAsync(dto.Id);
                if (entity.IsNull())
                {
                    return new OperationResult($"该{dto.Id}键的数据不存在", OperationResultType.QueryNull);
                }
                if (!checkFunc.IsNull())
                {
                    await checkFunc(dto, entity);
                }


                entity = dto.MapTo(entity);

                //entity.CreationTime = entity.CreationTime;
                if (!updateFunc.IsNull())
                {
                    await updateFunc(dto, entity);
                }

                entity = CheckModifiedTime(entity);
                await Task.Run(() => _dbSet.Update(entity));
                _dbContext.Entry(entity).Property(o => o.CreateTime).IsModified = false;


            }
            catch (CustomException e)
            {
                return new OperationResult(e.Message, OperationResultType.Error);
            }
            catch (Exception e)
            {
                //加日志
                _loggerHelper.Error(typeof(IRepository<TEntity, TPrimaryKey>), e);
                return new OperationResult(e.Message, OperationResultType.Exp);
            }


            int count = await _dbContext.SaveChangesAsync();


            return new OperationResult(count > 0 ? "更新成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
        }




        /// <summary>
        /// 按需更新
        /// </summary>
        /// <typeparam name="EntityDto">传入实体</typeparam>
        /// <param name="dto">实体</param>
        /// <param name="checkFunc">要检查</param>
        /// <param name="updateFunc">更新方法</param>
        /// <param name="properties"></param>
        /// <returns></returns>

        public virtual async Task<OperationResult> UpdateNotQueryAsync<EntityDto>(EntityDto dto, Func<EntityDto, TEntity, Task> checkFunc = null, Func<EntityDto, TEntity, Task<TEntity>> updateFunc = null, params Expression<Func<EntityDto, object>>[] properties)
        {
            dto.NotNull(nameof(dto));
            properties.NotNullOrEmpty(nameof(properties));
            try
            {
                var entity = dto.MapTo<TEntity>();
                if (!checkFunc.IsNull())
                {
                    await checkFunc(dto, entity);
                }

                if (!updateFunc.IsNull())
                {
                    await updateFunc(dto, entity);
                }

                var dbEntityEntry = _dbContext.Entry<TEntity>(entity);
                if (properties.Any())
                {
                    foreach (var property in properties)
                    {
                        var name = property.GetPropertyName();
                        dbEntityEntry.Property(name).IsModified = true;
                    }
                }
                else
                {
                    foreach (var rawProperty in dbEntityEntry.Entity.GetType().GetTypeInfo().DeclaredProperties)
                    {
                        var originalValue = dbEntityEntry.Property(rawProperty.Name).OriginalValue;
                        var currentValue = dbEntityEntry.Property(rawProperty.Name).CurrentValue;
                        foreach (var property in properties)
                        {

                            if (originalValue != null && !originalValue.Equals(currentValue))
                            {
                                var name = property.GetPropertyName();
                                dbEntityEntry.Property(property.Name).IsModified = true;
                            }

                        }

                    }
                }


                entity = CheckModifiedTime(entity);
            }
            catch (CustomException e)
            {
                return new OperationResult(e.Message, OperationResultType.Error);
            }
            catch (Exception e)
            {
                //加日志
                _loggerHelper.Error(typeof(IRepository<TEntity, TPrimaryKey>), e);
                return new OperationResult(e.Message, OperationResultType.Exp);
            }
            int count = await _dbContext.SaveChangesAsync();
            return new OperationResult(count > 0 ? "更新成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
        }

        /// <summary>
        /// 按需更新
        /// </summary>
        /// <param name="entity">要更新实体</param>
        /// <param name="properties">更新条件</param>
        /// <returns>返回操作结果</returns>

        public virtual OperationResult UpdateNotQuery(TEntity entity, params Expression<Func<TEntity, object>>[] properties)
        {
            entity.NotNull(nameof(entity));
            properties.NotNullOrEmpty(nameof(properties));
            var dbEntityEntry = _dbContext.Entry<TEntity>(entity);

            if (properties.Any())
            {
                foreach (var property in properties)
                {
                    dbEntityEntry.Property(property).IsModified = true;
                }
            }
            else
            {
                foreach (var rawProperty in dbEntityEntry.Entity.GetType().GetTypeInfo().DeclaredProperties)
                {
                    var originalValue = dbEntityEntry.Property(rawProperty.Name).OriginalValue;
                    var currentValue = dbEntityEntry.Property(rawProperty.Name).CurrentValue;
                    foreach (var property in properties)
                    {
                        if (originalValue != null && !originalValue.Equals(currentValue))
                            dbEntityEntry.Property(property).IsModified = true;
                    }

                }
            }
            int count = _dbContext.SaveChanges();
            return new OperationResult(count > 0 ? "更新成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
        }

        /// <summary>
        /// 检查修改时间
        /// </summary>
        private TEntity CheckModifiedTime(TEntity entity)
        {
            if (entity.ModifiedTime.HasValue)
            {
                entity.ModifiedTime = DateTime.Now;
            }

            return entity;
        }

        #endregion

        #region 删除

        /// <summary>
        /// 软删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<OperationResult> SoftDeleteAsync(TPrimaryKey id)
        {
            var entity = await GetAsync(id);
            if (entity.IsNull())
            {
                return new OperationResult($"该{id}键的数据不存在", OperationResultType.QueryNull);
            }
            entity.IsDelete = true;

            _dbContext.Update(entity);
            int count = await _dbContext.SaveChangesAsync();
            return new OperationResult(count > 0 ? "删除成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
        }

        /// <summary>
        /// 软删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<int> SoftDeleteBatchAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TEntity>> updateFactory, CancellationToken cancellationToken = default(CancellationToken))
        {
            predicate.NotNull(nameof(predicate));


            return  await  _dbSet.Where(predicate).UpdateAsync(updateFactory, cancellationToken);
            //var entity = await GetAsync(id);
            //if (entity.IsNull())
            //{
            //    return new OperationResult($"该{id}键的数据不存在", OperationResultType.QueryNull);
            //}
            //entity.IsDelete = true;

            //_dbContext.Update(entity);
            //int count = await _dbContext.SaveChangesAsync();
            //return new OperationResult(count > 0 ? "删除成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
        }

        /// <summary>
        /// 异步删除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="checkFunc"></param>
        /// <param name="delteFunc"></param>
        /// <returns></returns>
        public virtual async Task<OperationResult> DeleteAsync(TPrimaryKey key, Func<TEntity, Task> checkFunc = null, Func<TEntity, Task<TEntity>> delteFunc = null)
        {
            var entity = await GetAsync(key);
            if (entity.IsNull())
            {
                return new OperationResult($"该{key}键的数据不存在", OperationResultType.QueryNull);
            }

            try
            {
                if (!checkFunc.IsNull())
                {
                    await checkFunc(entity);
                }
                if (!checkFunc.IsNull())
                {
                    await delteFunc(entity);
                }
                _dbSet.Remove(entity);
            }
            catch (CustomException e)
            {
                return new OperationResult(e.Message, OperationResultType.Error);
            }
            catch (Exception e)
            {
                _loggerHelper.Error(typeof(IRepository<TEntity, TPrimaryKey>), e);
                return new OperationResult(e.Message, OperationResultType.Error);
            }
            int count = await _dbContext.SaveChangesAsync();
            return new OperationResult(count > 0 ? "删除成功" : "操作没有引发任何变化", count > 0 ? OperationResultType.Success : OperationResultType.NoChanged);
        }

        /// <summary>
        /// 异步删除所有符合特定条件的实体
        /// </summary>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <returns>操作影响的行数</returns>
        public async Task<int> DeleteBatchAsync(Expression<Func<TEntity, bool>> predicate)
        {
            predicate.NotNull(nameof(predicate));

      
            return await _dbSet.Where(predicate).DeleteAsync();
        }

        #endregion






        /// <summary>
        /// 异步检查实体是否存在
        /// </summary>
        /// <param name="predicate"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<bool> CheckExistsAsync(Expression<Func<TEntity, bool>> predicate, TPrimaryKey id = default(TPrimaryKey))
        {
            predicate.NotNull(nameof(predicate));

            TPrimaryKey defaultId = default(TPrimaryKey);
            var entity = await _dbSet.Where(predicate).Select(m => new { m.Id }).FirstOrDefaultAsync();
            bool exists = !typeof(TPrimaryKey).IsValueType && ReferenceEquals(id, null) || id.Equals(defaultId)
                ? entity != null
                : entity != null && !entity.Id.Equals(id);
            return exists;
        }

        public virtual TEntity GetFromChangeTrackerOrNull(TPrimaryKey id)
        {
            var entry = _dbContext.ChangeTracker.Entries()
                .FirstOrDefault(
                    ent =>
                        ent.Entity is TEntity &&
                        EqualityComparer<TPrimaryKey>.Default.Equals(id, ((TEntity)ent.Entity).Id)
                );

            return entry?.Entity as TEntity;
        }

        public void AttachIfNot(TEntity entity)
        {
            var entry = _dbContext.ChangeTracker.Entries().FirstOrDefault(ent => ent.Entity == entity);
            if (entry != null)
            {
                return;
            }

            _dbSet.Attach(entity);
        }

     
    }
}
